<template>
  <div v-loading="loading" class="pdf">
    <div class="pdf-toolbar">
      <el-button icon="menu" style="height: 28px;margin-right: 10px" @click="thumbnailPlanShow=!thumbnailPlanShow"/>
      <el-button-group>
        <el-button icon="arrowUp" style="height: 28px" @click="subPage"/>
        <el-button icon="arrowDown" style="height: 28px" @click="addPage"/>
      </el-button-group>
      <div style="display: flex;align-items: center">
        <el-input v-model="activePage" style="width: 58px;height: 28px;margin: 0 10px" @change="toPage(activePage)"/>
        /<span>{{ state.pageNum }}</span>
      </div>
      <div style="width: 70%;display: flex;justify-content: center; align-items: center;">
        <el-select v-model="state.scale" @change="selectChange" style="margin-left: 10px;height: 28px;width: 150px">
          <el-option
              v-for="item in scaleOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
          >
          </el-option>
        </el-select>
      </div>
      <div>
        <el-button v-if="downBtn" icon="download" style="height: 28px;" @click="download"/>
      </div>


    </div>
    <div class="pdf-content" @mousemove="planResize.planMouseMove" @mouseup="planResize.planMouseUp">
      <div v-show="thumbnailPlanShow" class="thumbnail-content" :style="{width:basicSize.planBasicWidth}">
        <div style="width: 100%;height: 100%">
          <div class="thumbnail-bar">
            <el-button icon="grid" @click="onShowThumbnailImage" style="width: 20px;height: 20px"/>
            <el-button @click="onShowOutLine" :disabled="!showOutLineBtn" icon="list" style="width: 20px;height: 20px"/>
          </div>
          <div style="  height: calc(100% - 30px);overflow: auto;">
            <div class="thumbnail-body">
              <div v-show="thumbnailShow" :title="`第${i+1}页`" v-for="(item,i) in thumbnailImage"
                   style="margin-top: 10px"
                   @click="toPage(i+1)">
                <div class="thumbnail-image" :class="[activePage===i+1?'selected':'']">
                  <el-image :src="item" style="height: 100%;width: 100%;"></el-image>
                  <div style="text-align: center">{{ i + 1 }}</div>
                </div>
              </div>
            </div>
            <div v-show="outLineShow && !thumbnailShow" class="directory-tree">
              <!-- 目录渲染 -->
              <el-tree
                  :data="treeData.outline"
                  :default-expand-all="true"
                  :expand-on-click-node="false"
                  :props="treeData.defaultProps" @node-click="data=>navigateTo(data.dest)">
              </el-tree>
            </div>
          </div>
        </div>
      </div>
      <div v-show="thumbnailPlanShow" ref="leftPlanResizeRef" class="left-border"
           @mousedown="planResize.planMousedDown"></div>
      <div ref="pdfContentRef" style="height: 100%;overflow-y: auto;flex: 1;padding: 20px 5px;"
           :style="{'--scale-factor':state.scale}">
        <div v-if="isReload">
          <div :id="`page_${page}`" v-for="page in state.pageNum" :key="page"
               class="pdf-item">
            <VuePdf :src="src" :page="page" :scale="state.scale"/>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import {nextTick, onMounted, reactive, computed} from "vue";
import * as pdfjsLib from "pdfjs-dist";
import {VuePdf, createLoadingTask} from 'vue3-pdfjs/esm';
import pdfFile from '@/assets/compressed.tracemonkey-pldi-09.pdf'

const props = defineProps({
  src: {
    type: String || Object,
    default: pdfFile
  },
  // fast :急速版，normal:正常（速度慢）
  model: {
    type: String,
    default: "normal"
  },
  downBtn: {
    type: Boolean,
    default: true
  }
})


const loading = ref(false)
const thumbnailPlanShow = ref(false)
const thumbnailShow = ref(true)
const outLineShow = ref(false)
const isReload = ref(true)
const thumbnailContainer = ref(null)


const treeData = reactive({
  outline: [],
  defaultProps: {
    children: 'items',
    label: 'title'
  }
});


const showOutLineBtn = computed(() => {
  return treeData.outline.length > 0
})

const thumbnailImage = ref([]);


const CSS = 96.0;
const PDF = 72.0;
const PDF_TO_CSS_UNITS = CSS / PDF;


const basicSize = reactive({
  planBasicWidth: '200px',
  scale: 1.33333
})

const scaleOptions = [
  {
    value: basicSize.scale,
    label: '适合页面',
  }, {
    value: basicSize.scale * 0.5,
    label: '50%',
  }, {
    value: basicSize.scale * 1.01,
    label: '100%',
  }, {
    value: basicSize.scale * 1.1,
    label: '125%',
  }, {
    value: basicSize.scale * 1.5,
    label: '150%',
  }, {
    value: basicSize.scale * 2,
    label: '200%',
  }
]

const selectChange = (val) => {
  isReload.value = false
  setTimeout(() => {
    isReload.value = true
  }, 100)
}
//是否在移动
const isMove = ref(false)

let pdfCtx = null
const state = reactive({
  pageNum: 0,
  scale: 1.33333
})

const pdfContentRef = ref(null)
const leftPlanResizeRef = ref(null)

const activePage = ref(1)


const subPage = () => {
  if (activePage.value > 1) {
    let tempNum = activePage.value
    activePage.value = tempNum - 1
    toPage(activePage.value)
  }
  console.log(activePage.value)
}
const addPage = () => {
  if (activePage.value < state.pageNum) {
    let tempNum = activePage.value
    activePage.value = tempNum + 1
    toPage(activePage.value)
  }
}
//点击缩略图按钮事件
const onShowThumbnailImage = () => {
  thumbnailShow.value = true
  outLineShow.value = false
}

//点击目录按钮事件
const onShowOutLine = () => {
  thumbnailShow.value = false
  outLineShow.value = true
}

//打开文件
const open = async (src) => {
  const pdf = await createLoadingTask(src).promise
  //赋值
  pdfCtx = pdf
  //获取总页面数
  state.pageNum = pdf.numPages
  //加载文件
  console.log(pdf);
  await renderPage(pdf)
}
//加载Pdf文件
const load = async (src) => {
  const loadingTask = getDocument({
    url: src,
    disableFontFace: true,   //禁用文本抗锯齿 ,提高渲染性能
  });
  return await loadingTask.promise;
}

//加载页数据
const renderPage = async (pdf) => {
  // 异步获取目录树
  setTimeout(async () => {
    let outLine = await pdf.getOutline();
    if (outLine) {
      treeData.outline = outLine
    }
  }, 0)
  for (let i = 1; i <= pdf.numPages; i++) {
    //获取页面
    let page = await pdf.getPage(i)
    //预览试图
    let thumbnailViewport = page.getViewport({scale: 1});
    await renderThumbnailImage(page, thumbnailViewport)
  }
  loading.value = false
}

//生成缩略图
const renderThumbnailImage = async (page, viewport) => {
  let canvas = document.createElement("canvas");
  canvas.width = viewport.width;
  canvas.height = viewport.height;
  const ctx = canvas.getContext("2d");
  const renderContext = {
    canvasContext: ctx,
    viewport: viewport,
  };
  await page.render(renderContext).promise;
  thumbnailImage.value.push(canvas.toDataURL("image/png"))
}


//目录跳转
const navigateTo = async (dest) => {
  if (!pdfCtx || !dest) {
    return;
  }
  // 获取目标页面的页码
  const pageNumber = (await pdfCtx.getPageIndex(dest[0])) + 1;
  toPage(pageNumber)
}

const toPage = (pageNumber) => {
  // 滚动到目标页面
  const pageElement = document.getElementById(`page_${pageNumber}`);
  if (pageElement) {
    pageElement.scrollIntoView({behavior: "smooth"});
  }
}


//Pdf滚动监听
const scrollEventListen = (e) => {
  //滚动条高度
  let scrollTop = e.target.scrollTop
  //计算单页面高度
  let pdfFirst = document.querySelectorAll(".pdf-item")[0];
  if (pdfFirst) {
    //计算当前activePage
    activePage.value = Math.trunc(scrollTop / pdfFirst.clientHeight) + 1
  }
}

async function download(e) {
  let url = "";
  if (typeof props.src === "string") {
    url = props.src
  } else {
    url = URL.createObjectURL(props.src)
  }
  let filename = pdfjsLib.getFilenameFromUrl(url)
  const data = await pdfCtx.getData();
  const blob = new Blob([data], {
    type: "application/pdf"
  });

  const blobUrl = URL.createObjectURL(blob);

  const a = document.createElement("a");
  if (!a.click) {
    throw new Error('DownloadManager: "a.click()" is not supported.');
  }
  a.href = blobUrl;
  a.target = "_parent";
  if ("download" in a) {
    a.download = filename;
  }
  (document.body || document.documentElement).append(a);
  a.click();
  a.remove();
}


//拖动改变面板大小
const planResize = {
  //鼠标按下
  planMousedDown: (e) => {
    isMove.value = true
  },
  //鼠标移动
  planMouseMove: (e) => {
    if (isMove.value && e.layerX >= 200) {
      basicSize.planBasicWidth = `${e.layerX}px`
    }
  },
  //鼠标松开
  planMouseUp: (e) => {
    isMove.value = false
  }
}

onMounted(() => {
  nextTick(() => {
    loading.value = true
    open(props.src)
    //添加滚动监听
    let pdfContent = pdfContentRef.value;
    pdfContent.addEventListener("scroll", scrollEventListen, false)
  })
})
//抛出页面跳转方法
defineExpose({
  toPage
})


</script>

<style scoped lang="scss">


.thumbnail {


  &:hover {
    background-color: #adadaf;
    background-clip: padding-box;
    color: rgba(255, 255, 255, 1);
  }

  &-content {
    overflow-y: scroll;
    min-width: 200px;
    display: flex;
    flex-wrap: wrap;
  }

  &-bar {
    height: 30px;
    width: 100%;
    background-color: #f5f6f7;
    padding-left: 10px;
    margin-right: 5px;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    flex-direction: row;
  }

  &-body {
    width: 100%;
    padding: 0 30px;
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
  }

  &-image {
    width: 100px;
    height: 128px;
    border-radius: 2px;
    padding: 7px;
  }
}

.pdf {
  display: flex;
  height: 100%;
  position: relative;
  background-color: #d4d4d7;
  flex-direction: column;

  &-toolbar {
    width: 100%;
    height: 40px;
    background-color: #f9f9fa;
    padding-left: 10px;
    display: flex;
    align-items: center;
    box-sizing: border-box;
    border: 1px solid #cecccc;
    position: relative;

  }

  &-content {
    display: flex;
    height: calc(100% - 40px);
  }

  &-item {
    margin: 10px auto;
    width: calc(var(--scale-factor) * 612px);
    /*height: calc(var(--scale-factor) * 792px);*/
  }
}

.left-border {
  width: 3px;
  height: 100%;
  cursor: e-resize
}

.selected {
  background-color: #adadaf;
  background-clip: padding-box;
  color: rgba(255, 255, 255, 1);
}

:deep(.el-tree-node.is-current  .el-tree-node__content) {
  color: #cc2722 !important;
  font-weight: 600;
}

:deep(.el-tree-node:focus .el-tree-node__content) {
  background-color: #fff !important;
}

:deep(.el-tree-node__content:hover) {
  background-color: #fff !important;
}

:deep(.el-icon-caret-right:before) {
  content: "";
}

:deep(.el-tree-node__content) {
  height: 40px;
}

:deep(.el-tree-node__label) {
  line-height: 40px;
}
</style>
